package org.zw.android.framework.async;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import android.os.Message;

/**
 * Asynchronous Task Executor; Android System require 4.0
 * 
 * @author zhouwei
 *
 */
public final class AsyncTaskExecutor {
	
	// task status
	public static enum TaskStatus {
		WAITING, START , PROCESSING , COMPLETED , FAILED ,  ERROR , CANCEL ;
	}
	
	private static final int CORE_POOL_SIZE 	= 5;
    private static final int MAXIMUM_POOL_SIZE 	= 128;
    private static final int KEEP_ALIVE 		= 1;
    
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
    
    private static final ThreadFactory sSingleThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "Single AsyncTask #" + mCount.getAndIncrement());
        }
    };
    
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);
    
    // Task Queue
    private static final List<AbsractTask> sTaskQueue = new ArrayList<AbsractTask>();
    
    // multi-executor
    private static final ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, 
    		MAXIMUM_POOL_SIZE, 
    		KEEP_ALIVE,
            TimeUnit.SECONDS, 
            sPoolWorkQueue, 
            sThreadFactory);
    
    // single executor
    private static final ExecutorService mSingleExecutor = Executors.newSingleThreadExecutor(sSingleThreadFactory);
    
    private AsyncTaskExecutor(){
    	
    }
    
    /**
     * Execute Task in thread pool
     * 
     * @param task
     * @return
     */
	public static Worker execute(AsyncTaskRunnable task){
		
		// TaskMonitor is null
		if(task == null) return null ;
		
		// ThreadPoolExecutor isShutdown
		if(mExecutor.isShutdown()){
			mExecutor.prestartAllCoreThreads() ;
		}
		
		// create new task
		AbsractTask t 		= new AbsractTask(task) ;
		WorkerImp worker 	= new WorkerImp(t);
		// attach worker
		if(task.getHandler() != null){
			task.getHandler().attachWorker(worker);
		}
		
		// execute
		t.executeOnExecutor(mExecutor,"");
		
		sTaskQueue.add(t);
		
		return worker ;
	}
	
	/**
	 * 
	 * @param task
	 * @return
	 */
	public static Worker executeSingleTask(AsyncTaskRunnable task){
		
		// TaskMonitor is null
		if(task == null) return null ;
		
		// create new task
		AbsractTask t 		= new AbsractTask(task) ;
		WorkerImp worker 	= new WorkerImp(t);
		t.executeOnExecutor(mSingleExecutor,"");
		
		sTaskQueue.add(t);
		
		return worker ;
	}
	
	public static void cancelAllTask(){
		
		try{
			
			for(AbsractTask t : sTaskQueue){
				t.cancel(true);
			}
			
			sTaskQueue.clear() ;
			mExecutor.shutdownNow() ;
			mExecutor.purge() ;
			
		} catch(Exception e){
			e.printStackTrace() ;
		}
	}
	
	private static final class AbsractTask extends AsyncTask<String, String, Object> {
		
		private AsyncTaskRunnable 	mTask ;
		private TaskStatus			mStatus ;
		private AsyncTaskHandler	mHandler ;
		
		protected AbsractTask(AsyncTaskRunnable task){
			mTask 		= task ;
			mStatus 	= TaskStatus.WAITING ;
			mHandler 	= task.getHandler() ;
		}

		// start task
		@Override
		protected void onPreExecute() {
			
			// status
			mStatus	= TaskStatus.PROCESSING ;
			
			// Interrupt Task
			if(!mTask.onStart()){
				mStatus	= TaskStatus.CANCEL ;
				cancel(true);
			} else {
				
				// handler start message
				if(mHandler != null) {
					Message msg = mHandler.obtainMessage(AsyncTaskHandler.MSG_STATUS_START);
					mHandler.handleMessage(msg);
				}
			}
		}

		// processing
		@Override
		protected Object doInBackground(String... params) {
			
			Object obj 	= null ;
			
			try{
				
				// process task
				obj = mTask.onProcessing() ;
				
				// on finish
				mTask.onFinish(obj);
				
				// current task completed
				mStatus		= obj != null ? TaskStatus.COMPLETED : TaskStatus.FAILED ;
				
			} catch(Exception e){
				e.printStackTrace() ;
				
				// on error
				mStatus		= TaskStatus.ERROR ;
				mTask.onException(e) ;
				
				// send finish message
				if(mHandler != null) {
					mHandler.sendMessage(AsyncTaskHandler.MSG_STATUS_ERROR,e);
				}
			}
			
			// remove from task queue
			sTaskQueue.remove(this);
			
			return obj;
		}
		
		// end task
		@Override
		protected void onPostExecute(Object result) {
			
			// send execute completed message
			if(mStatus == TaskStatus.COMPLETED
					&& mHandler != null) {
				Message msg = mHandler.obtainMessage(AsyncTaskHandler.MSG_STATUS_FINISH,result);
				mHandler.handleMessage(msg);
			}
			
			// handler task end message
			if(mHandler != null) {
				Message msg = mHandler.obtainMessage(AsyncTaskHandler.MSG_STATUS_COMPLETED);
				mHandler.handleMessage(msg);
			}
		}

		@Override
		protected void onCancelled() {
			
			// send finish message
			if (mHandler != null) {
				mHandler.sendMessage(AsyncTaskHandler.MSG_STATUS_CANCEL);
			}
			
			// cancel
			mTask.onCancel() ;
			
			// remove from task queue
			sTaskQueue.remove(this);
		}
	}
	
	private static class WorkerImp extends Worker {
		
		private AbsractTask mTask ;
		
		protected WorkerImp(AbsractTask task){
			mTask	= task ;
		}
		
		@Override
		public void onCancelled() {
			if(mTask != null) mTask.cancel(true);
		}

		@Override
		public boolean isCancelled() {
			return mTask != null ? mTask.isCancelled() : true ;
		}
	}
}
